/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Estimator;

import OLDSTUFF.ClassAlgebra.ClassAlgebraSingleton;
import OLDSTUFF.ClassAlgebra.ComplexExpression;
import OLDSTUFF.ClassAlgebra.ComplexProbExpr;
import OLDSTUFF.ClassAlgebra.EmptyExpression;
import OLDSTUFF.ClassAlgebra.Expression;

import OLDSTUFF.ClassAlgebra.ProbAlgebraSingleton;
import OLDSTUFF.ClassAlgebra.ProbExpr;
import OLDSTUFF.ClassAlgebra.SimpleExpression;
import OLDSTUFF.ClassAlgebra.SimpleProbExpr;
import OLDSTUFF.ClassAlgebra.ZeroProbExpr;
import OLDSTUFF.DataSetPackage.DataSetException;
import OLDSTUFF.DataSetPackage.ProductView;
import OLDSTUFF.DataSetPackage.View;
import OLDSTUFF.HierarchyPackage.HClass;
import OLDSTUFF.HierarchyPackage.Hierarchy;
import OLDSTUFF.HierarchyPackage.HierarchyException;
import OLDSTUFF.HierarchyPackage.ProductHierarchy.ProductHClass;
import OLDSTUFF.HierarchyPackage.ProductHierarchy.ProductHierarchy;
import OLDSTUFF.HierarchyPackage.AffixHierarchies.SuffixHClass;
import OLDSTUFF.HierarchyPackage.AffixHierarchies.SuffixHierarchy;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Martin Haulrich
 */
public class IndependentProductEst implements OLDSTUFF_EstimatorExp {

    OLDSTUFF_EstimatorExp[] partEstimator;

    int cH = 0;

    Map<Expression, ProbExpr> cache;

    public IndependentProductEst(OLDSTUFF_EstimatorExp[] partEstimator) {
        this.partEstimator = partEstimator;

        cache = new LinkedHashMap<Expression, ProbExpr>(1280, 0.75F, true) {

            @Override
            protected boolean removeEldestEntry(Map.Entry<Expression, ProbExpr> eldest) {
                return size() > 1000;
            }
        };

    }

    @Override
    public double getEstimate(Expression pe) throws HierarchyException {
        //    System.err.println("hej");
        // We convert the ecpression to a probability-expression and use ProbAlgebra to try and reduce it
        //throw new HierarchyException("should not be called");
        //      System.out.println("called");
        ProbExpr npe = cache.get(pe);
        if (npe == null) {
            //npe = ProbAlgebra.map(new SimpleProbExpr(pe));
            npe = ProbAlgebraSingleton.getInstance().map(new SimpleProbExpr(pe));
            cache.put(pe, npe);
        }
//        else {
//            System.out.println("    hit");
//        }



        System.err.println("Mapped " + pe + " to " + npe);
        return getEstimateRec(npe, null);


    }

    @Override
    public double getEstimate(Expression pe, Hierarchy hier) throws HierarchyException {
        //     System.err.println("hej");
        // We convert the ecpression to a probability-expression and use ProbAlgebra to try and reduce it

        //System.err.println("HEJ");
//        ProbExpr npe = ProbAlgebra.map(new SimpleProbExpr(pe), hier);
        //   System.out.println("called");
        ProbExpr npe = cache.get(pe);
        if (npe == null) {
            //  npe = ProbAlgebra.map(new SimpleProbExpr(pe), hier);
            npe = ProbAlgebraSingleton.getInstance().map(new SimpleProbExpr(pe), hier);
            cache.put(pe, npe);
        }
//        else {
//            System.out.println("    hit");
//        }


        Double est;// = cache.get(npe);
//        if (est!=null) {
////            cH++;
////            System.err.println("cachehit nr: " + cH);
//            return est;
//        }
        //System.err.println("Mapped " + pe + " to " + npe);
        est = getEstimateRec(npe, (ProductHierarchy) hier);

        //System.err.println("IP: " + npe + "  === "+ est);
        //   cache.put(npe, est);
        return est;


    }

    private double getEstimateRec(ProbExpr pe, ProductHierarchy hier) throws HierarchyException {

        //System.err.println("PE: " + pe);
        if (pe instanceof ZeroProbExpr) {
            return -20000000;
        }

        if (pe instanceof SimpleProbExpr) {
            //     System.err.println("SimpleProp");
            Expression ce = ((SimpleProbExpr) pe).e;
            //return getClassEst(ClassAlgebra.map(ce), hier);
            return getClassEst(ClassAlgebraSingleton.getInstance().map(ce), hier);
        }

        ComplexProbExpr cpe = (ComplexProbExpr) pe;
        if (cpe.operation == ComplexProbExpr.DIFFERENCE) {
            double se1 = getEstimateRec(cpe.pe1, hier);
            //   System.err.println("res se1: " + se1 + "\t" + cpe.pe1);
            double se2 = getEstimateRec(cpe.pe2, hier);
            //  System.err.println("res se2: " + se2 + "\t" + cpe.pe2);
            double l = Math.max(se1, se2);
            double res = l + Math.log10(Math.pow(10, se1 - l) - Math.pow(10, se2 - l));
            //      System.err.println("diff res: " + res + "\t" + se1 + " ; " + se2+"\t\t" + Math.pow(10,se1) + " ; "  + Math.pow(10,se2));
            return res;
        }
        if (cpe.operation == ComplexProbExpr.SUM) {
            double se1 = getEstimateRec(cpe.pe1, hier);
            double se2 = getEstimateRec(cpe.pe2, hier);
            double l = Math.max(se1, se2);
            double res = l + Math.log10(Math.pow(10, se1 - l) + Math.pow(10, se2 - l));
            // System.err.println("sum res: " + res);
            return res;

        }

        System.err.println("SuffixEstimatorExp.getEstimateRec\nThis should not happen!");

        return 0;
    }

    private double getClassEst(Expression e, ProductHierarchy hier) throws HierarchyException {
        //      System.err.println("G: " + e);
        if (e instanceof SimpleExpression) {
            //         System.err.println("Returnig simple");
            return getEstimate((ProductHClass) ((SimpleExpression) e).c, hier);
        }
        if (e instanceof EmptyExpression) {
            return -2000000000;
        }
        if (e instanceof ComplexExpression) {
            ComplexExpression ce = (ComplexExpression) e;
            if (ce.operation == ComplexExpression.INTERSECTION) {
                /**
                 * @TODO IS this right???
                 */
                if ((ce.e1 instanceof SimpleExpression) && (ce.e2 instanceof SimpleExpression)) {
                    SimpleExpression e1 = (SimpleExpression) ce.e1;
                    SimpleExpression e2 = (SimpleExpression) ce.e2;
                    HClass c1 = e1.c;
                    HClass c2 = e2.c;

                    // Return the probabiblity of the longest suffix unless the two suffixes are disjunctive. In that case return 0;
                    if (c1.belongsTo().superclassOf(c1, c2)) {
                        return getClassEst(e2, hier);

                    }
                    if (c1.belongsTo().superclassOf(c2, c1)) {
                        return getClassEst(e1, hier);
                    }

                    return -20000000;
                }
            }


        }


        System.err.println("Independent.getClassEst");
        System.err.println("I THINK that this should not happen because all expression should be reduced until there are only");
        System.err.println("simpleexpression and intersection of simple expressions left");
        System.err.println("Here the expression is: " + e);
        throw new UnsupportedOperationException();
    }

    public double getEstimate(ProductHClass h, ProductHierarchy hier) throws HierarchyException {

        double est = 0;

        HClass[] classes = h.getID();

        for (int i = 0; i < classes.length; i++) {

            Expression ce = new SimpleExpression(classes[i]);
            double cest = partEstimator[i].getEstimate(ce, hier);
            //      System.err.println(classes[i].getName() + " : " + cest);
            est = est + cest;
        }
//
//        HClass[] newClasses = new HClass[classes.length];
//        System.arraycopy(classes, 0, newClasses, 0, classes.length);
//        newClasses[classes.length - 1] = hier.getHierarchies()[classes.length - 1].getTop();
//        double condEst = getEstimateAbs(new ProductHClass(hier, newClasses), hier);
//
//        return est - condEst;
//        int index = classes.length-1;
//        return partEstimator[index].getEstimate(new SimpleExpression(classes[index]), hier);
        return est;
    }

//    public double getEstimateAbs(ProductHClass h, ProductHierarchy hier) throws HierarchyException {
//
//        double est = 0;
//
//        HClass[] classes = h.getID();
//
//        for (int i = 0; i < classes.length; i++) {
//
//            Expression ce = new SimpleExpression(classes[i]);
//            double cest = partEstimator[i].getEstimate(ce, hier);
//            //      System.err.println(classes[i].getName() + " : " + cest);
//            est = est + cest;
//        }
//
//        return est;
//    }

    public double getEstimate(List<String> ngram, ProductHierarchy h) throws HierarchyException {
        double est = 0;
        for (int i = ngram.size()-1; i < ngram.size(); i++) {
            double thisest = partEstimator[i].getEstimate(new SimpleExpression(new SuffixHClass((SuffixHierarchy) h.getHierarchies()[i], ngram.get(i))),h.getHierarchies()[i]);
            est = est + thisest;
        //SimpleView sv = (SimpleView) views[i];
        //      System.err.println("DataEst-"+i+": " +dids[i] + "    "+ sv.getFeatures(dids[i])[0] + " : " + thisest);
        }
//
//        double condEst = 0;
//        for (int i = 0; i < dids.length - 1; i++) {
//            double thisest = partEstimator[i].getEstimate(dids[i], views[i]);
//            condEst = condEst + thisest;
//        //SimpleView sv = (SimpleView) views[i];
//        //      System.err.println("DataEst-"+i+": " +dids[i] + "    "+ sv.getFeatures(dids[i])[0] + " : " + thisest);
//        }
//
//        return est - condEst;
//        int index = dids.length-1;
//        return partEstimator[index].getEstimate(dids[index], views[index]);
        return est;
    }

    @Override
    public double getEstimate(Integer did, View v) throws DataSetException, HierarchyException {
        ProductView pv = (ProductView) v;
//        int[] dids = pv.getDataIDs(did);
        View[] views = pv.getViews();
        double est = 0;
        for (int i = 0; i < views.length; i++) {
            double thisest = partEstimator[i].getEstimate(did, views[i]);
            est = est + thisest;
        //SimpleView sv = (SimpleView) views[i];
        //      System.err.println("DataEst-"+i+": " +dids[i] + "    "+ sv.getFeatures(dids[i])[0] + " : " + thisest);
        }
//
//        double condEst = 0;
//        for (int i = 0; i < dids.length - 1; i++) {
//            double thisest = partEstimator[i].getEstimate(dids[i], views[i]);
//            condEst = condEst + thisest;
//        //SimpleView sv = (SimpleView) views[i];
//        //      System.err.println("DataEst-"+i+": " +dids[i] + "    "+ sv.getFeatures(dids[i])[0] + " : " + thisest);
//        }
//
//        return est - condEst;
//        int index = dids.length-1;
//        return partEstimator[index].getEstimate(dids[index], views[index]);
        return est;
    }
}
